/*
 * SubManager.java
 */

package subplay;

import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
import java.util.Vector;

/**
 *
 * @author dominiks
 */
public class SubManager {
    
    /** Creates a new instance of SubManager */
    public SubManager() throws IOException{
        this("/dumpsub.sub");
    }
    private SubManager(String subname)throws IOException {
        InputStreamReader re = new InputStreamReader(getClass().getResourceAsStream(subname), "UTF-8");
        char[] boom = new char[65535];
        int len = re.read(boom);
        int l = 0, p = -1;
        do {
            for (l = p + 1; l < len && boom[l] != '\n'; l++) {
                
            }
            if (l < len && l > p + 1) {
                String line = new String(boom, p + 1, l - p - 1);
                line.trim();
                int e1 = line.indexOf('}');
                if (e1 == -1) {
                    continue;
                }
                int l1 = Integer.parseInt(line.substring(1, e1));
                String text = line.substring(line.indexOf('}', e1 + 1) + 1);
                texts.addElement(text);
                times.addElement(new Long(l1));
            }
            p = l;
        } while (l < len);
        begTime=new Date().getTime();
    }
    
    
    
    //------------------------------------------------------------
    Vector texts = new Vector();
    Vector times = new Vector();
    /**
     * framerate coeff/div shadow value
     */
    long coeff;
    long div;
    
    
    
    public String tx(int i) {
        String ret;
        if(i<0 || i>= texts.size())
            ret= "---";
        else
            ret= (String)texts.elementAt(i);
        return ret;
    }
    public int size(){
        return texts.size();
    }
    public int mSize(){
        return timepos.size();
    }
    
    public synchronized int getIndex(long ct){
        long time = 0;
        int ind;
        time = (ct - begTime)/100;
        
        long destTimePos ;
        destTimePos= a0/delta + (coeff * time)/div;
        System.out.print("time:" + time + ",dest:" + destTimePos);
        for (int i = 0; i < times.size(); i++) {
            long t = ((Long) times.elementAt(i)).longValue();
            if (t > destTimePos) {
                ind = i - 1;
                System.out.print(",foundind:" + ind);
                if (i < 0) {
                    i = 0;
                }
                return ind;
            }
        }
        return 0;
    }
    public void latch(){
        if(isAutoSuitable()){
            coeff=last_q;
            div=10000;
        }else{
            coeff=a1;
            div=delta;
        }
    }
    /*
    public void setMode(int mode){
        long ncoeff;
        long ndiv;
     
        if(mode==0){
            if(isAutoSuitable()){
                ncoeff=last_q;
                ndiv=10000;
            }else{
                ncoeff=a1;
                ndiv=delta;
            }
        }else{
            ncoeff=quantizators[mode-1];
            ndiv=10000;
        }
        if(coeff==0){
            coeff=ncoeff;
            div=ndiv;
            offset=a0;
        }else{
            //offset=a0
        }
    }
     */
    
    //---------------------------------Callibration code------------------------------
    Vector timepoints = new Vector(20);
    Vector timepos = new Vector(20);
    
    
    /**Time of starting playing.
     *It is later manually adjustable time.
     *More important in fixed FR where a0 is constant and this is adjustable.
     */
    long begTime;
    /*Coefficients computed from the measurements*/
    long a0, a1;
    long delta;
    
    /**
     * returns frame rate computed from mesurements
     * sets a0, a1 and delta
     */
    public synchronized int compute() {
        int n = timepoints.size();
        if(n<2)
            return 0;
        long s0 = n;
        long s1 = 0, s2 = 0, t0 = 0, t1 = 0;
        for (int i = 0; i < timepoints.size(); i++) {
            long x = ((Long) timepoints.elementAt(i)).longValue();
            System.out.println(x);
            long y = ((Long) timepos.elementAt(i)).longValue();
            s1 += x;//432000000
            s2 += x * x; // 6220800000000000
            t0 += y; // 216000
            t1 += x * y; //31104000000000
        }
        delta = s0 * s2 - s1 * s1;
        a0 = (t0 * s2 - t1 * s1) ;
        a1 = (t1 * s0 - t0 * s1) ;
        System.out.println("n:" + n + ",s0:" + s0 + ",s1:" + s1 + ",s2:" + s2 + ",t0:" + t0 + ",t1:" + t1 + ",delta:" + delta + ",a0:" + a0 + ",a1:" + a1);
        return (int)(a1*10000/delta);
    }
    
    
    class Measure {
        
        public long time;
        Long timePoint;
        
        Measure(int ind) {
            long ct = new Date().getTime();
            time = (ct - begTime)/100;
            timePoint = (Long) times.elementAt(ind);
        }
    }
    
    public Measure newMeasure(int i){
        return new Measure(i);
    }
    
    
    int last_q;
    int last_q_tenure;
    public synchronized void addMeasure(Measure m) {
        timepoints.addElement(new Long(m.time));
        timepos.addElement(m.timePoint);
        System.out.println("time:" + m.time + ",disp:" + m.timePoint);
        int q=compute();
        q=quantize(q);
        if(q!=-1){
            if(last_q==0){
                last_q=q;
            }else{
                if(last_q==q)
                    last_q_tenure++;
                else{
                    last_q=q;
                    last_q_tenure=0;
                }
            }
        }
    }
    synchronized  public void adjustOffest(Measure m){
        long real_time=m.time;
        long tp=m.timePoint.longValue();
        long thought_time=(tp-a0/delta)*div/coeff;
        long time_dif=real_time-thought_time;
        begTime+=time_dif*100;
    }
    
    static int[] quantizators=new int[]{
        15000,
        20000,
        23976,
        /*23978,*/
        24000,
        25000,
        29970,
        30000
    };
    static int[] errors=new int[]{
        2500,
        1950,
        15,
        /*23978,*/
        15,
        500,
        15,
        15
    };
    public static int err;
    public static int quantize(int value){
        int min_dist=Integer.MAX_VALUE;
        int min_ind=0;
        for(int i=0;i<quantizators.length;i++){
            int d=(int)Math.abs(quantizators[i]-value);
            if(d<min_dist){
                min_dist=d;
                min_ind=i;
            }
        }
        if(min_dist<errors[min_ind]){
            err=100;
            return quantizators[min_ind];
        }else{
            err=errors[min_ind]*100/min_dist;
        }
        return -1;
    }
    public boolean isAutoSuitable(){
        return last_q_tenure>5;
    }
    public synchronized void clearMeasures(){
        timepoints.setSize(0);
        timepos.setSize(0);
    }
    public synchronized void clearMeasures(int pop){
        if(mSize()<pop){
            timepoints.setSize(0);
            timepos.setSize(0);
        }else{
            for(int i=mSize()-1;pop>0;pop--,i--){
                timepoints.removeElementAt(i);
                timepos.removeElementAt(i);
            }
        }
    }
}

